package com.gwtjs.hbm2xml.spring;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;

import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

public class Helper {
	private static Helper helper = new Helper();

	private Helper() {
	}

	public static Helper getInstance() {
		return helper;
	}

	private Attribute getNamespace(Namespace xsi) {
		StringBuilder sb = new StringBuilder();
		sb.append("http://www.springframework.org/schema/beans ");
		sb.append("http://www.springframework.org/schema/beans/spring-beans-3.1.xsd ");
		sb.append("http://www.springframework.org/schema/aop ");
		sb.append("http://www.springframework.org/schema/aop/spring-aop-3.1.xsd ");
		sb.append("http://www.springframework.org/schema/tx ");
		sb.append("http://www.springframework.org/schema/tx/spring-tx-3.1.xsd ");
		sb.append("http://www.springframework.org/schema/context ");
		sb.append("http://www.springframework.org/schema/context/spring-context-3.1.xsd");

		return new Attribute("schemaLocation", sb.toString(), xsi);
	}

	/**
	 * @param ns
	 * @return <bean id="placeholderConfig" ...
	 */
	private Element getPlaceholderConfig(Namespace ns) {
		Element proConfig = new Element("bean", ns);
		proConfig.setAttribute("id", "placeholderConfig");
		proConfig
				.setAttribute("class",
						"org.springframework.beans.factory.config.PropertyPlaceholderConfigurer");

		Element pro = new Element("property", ns);
		pro.setAttribute("name", "location");
		proConfig.addContent(pro);

		Element val = new Element("value", ns);
		val.addContent("classpath:hibernate.properties");
		pro.addContent(val);

		return proConfig;
	}

	/**
	 * @param ns
	 * @param key
	 * @param value
	 * @return <property name="maxIdleTime" value="${c3p0.maxIdleTime}" />
	 */
	private Element createProperty(Namespace ns, String key, String value) {
		return this.createProperty(ns, key, value, null);
	}

	/**
	 * @param ns
	 * @param key
	 * @param value
	 * @return <property name="maxIdleTime" value="${c3p0.maxIdleTime}" />
	 */
	private Element createProperty(Namespace ns, String key, String value,
			String name) {
		Element e = new Element("property", ns);
		if (null != name)
			e = new Element(name, ns);

		String nameAttr = key;
		if (key.startsWith("hibernate.")||key.indexOf(".")==-1)
			nameAttr = key;
		else 
			nameAttr = key.substring(key.indexOf(".")+1);
		
		e.setAttribute("name", nameAttr);
		e.setAttribute("value", "${" + key + "}");// value
		return e;
	}

	/**
	 * @param ns
	 * @param name
	 * @return <property name="xxx">
	 */
	private Element getChildProperty(Namespace ns, String name) {
		Element ep = new Element("property", ns);
		ep.setAttribute("name", name);
		return ep;
	}

	/**
	 * @param name
	 *            ele name
	 * @return <list>
	 */
	private Element createElement(Namespace ns, String name) {
		return this.createElement(ns, name, null);
	}

	/**
	 * @param name
	 *            ele name
	 * @return <value>com/gwtjs/model/ACL.hbm.xml</value>
	 */
	private Element createElement(Namespace ns, String name, String text) {
		Element e = new Element(name, ns);
		if (null != text)
			e.setText(text);
		return e;
	}

	/**
	 * @param fileName
	 *            ...\DocumentItem.hbm.xml
	 * @return DocumentItem.hbm.xml
	 */
	private String getFileName(String fileName) {
		return "hbm/"+fileName.substring(fileName.lastIndexOf("\\") + 1);
	}

	/**
	 * @param ns
	 * @return <bean id="sessionFactory" ...
	 */
	private Element createSessionFactoryElement(Namespace ns,
			List<String> hbms, String propertyFile) {
		String id = "sessionFactory";
		String cls = "org.springframework.orm.hibernate3.LocalSessionFactoryBean";
		Element e = new Element("bean", ns);
		e.setAttribute("id", id);
		e.setAttribute("class", cls);

		Element ep = getChildProperty(ns, "dataSource");

		Element epo = new Element("ref", ns);
		epo.setAttribute("local", "dataSource");

		ep.addContent(epo);
		e.addContent(ep);

		ep = getChildProperty(ns, "mappingResources");
		Element list = createElement(ns, "list");
		// 创建<value>com/gwtjs/model/ACL.hbm.xml</value>
		for (String s : hbms) {
			Element val = createElement(ns, "value", getFileName(s));
			list.addContent(val);
		}
		ep.addContent(list);
		e.addContent(ep);

		ep = getChildProperty(ns, "hibernateProperties");
		Element props = createElement(ns, "props");

		Enumeration<?> enu = null;
		Properties pros = null;
		try {
			InputStream is = new FileInputStream(propertyFile);
			pros = new Properties();

			try {
				pros.load(is);
				is.close();
				enu = pros.propertyNames();
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		while (enu.hasMoreElements()) {
			String key = (String) enu.nextElement();
			if (!key.startsWith("dataSource.")) { //这边就取反，不是dataSource.开始的就添加
				String value = pros.getProperty(key);
				props.addContent(createProperty(ns, key, value, "prop"));
			}
		}
		ep.addContent(props);
		e.addContent(ep);

		return e;
	}

	/**
	 * @param ns
	 * @param propertyFile
	 * @return <bean id="dataSource" ...
	 */
	private Element getDataSourceNode(Namespace ns, String propertyFile) {
		Element e = new Element("bean", ns);
		e.setAttribute("id", "dataSource");
		e.setAttribute("class", "com.mchange.v2.c3p0.ComboPooledDataSource");
		e.setAttribute("destroy-method", "close");

		Enumeration<?> enu = null;
		Properties pros = null;
		try {
			InputStream is = new FileInputStream(propertyFile);
			pros = new Properties();

			try {
				pros.load(is);
				is.close();
				enu = pros.propertyNames();
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		while (enu.hasMoreElements()) {
			String key = (String) enu.nextElement();
			if (key.startsWith("dataSource.")) {
				String value = pros.getProperty(key);
				e.addContent(createProperty(ns, key, value));
			}
		}

		return e;
	}

	/**
	 * @param tx
	 *            引用命名空间
	 * @param ename
	 *            标签名
	 * @param attrName
	 *            属性名
	 * @param value
	 *            属性值
	 * @return '<tx:method name="save*" propagation="REQUIRED" />'
	 */
	private Element createMethod(Namespace tx, String ename, String attrName,
			boolean flag) {
		Element method = new Element(ename, tx);
		method.setAttribute("name", attrName);
		method.setAttribute("propagation", "REQUIRED");
		if (flag)
			method.setAttribute("read-only", "true");
		return method;
	}

	/**
	 * @param tx
	 * @return <tx:advice id="txAdvice" ......
	 */
	private Element createAdvice(Namespace tx) {
		Element advice = new Element("advice", tx);
		advice.setAttribute("id", "advice");
		advice.setAttribute("transaction-manager", "transactionManager");
		Element attributes = new Element("attributes", tx);
		List<TXMethod> list = TXMethod.getInstance().getMethod();
		for (TXMethod t : list) {
			attributes.addContent(createMethod(tx, "method", t.getName(),
					t.isFlag()));
		}
		advice.addContent(attributes);
		return advice;
	}

	/**
	 * @param ns
	 * @param p
	 * @return <bean id="transactionManager" .... ......
	 */
	private Element createTransactionManager(Namespace ns, Namespace p) {
		Element bean = new Element("bean", ns);
		Attribute a = new Attribute("sessionFactory-ref", "sessionFactory", p);
		bean.setAttribute(a);
		bean.setAttribute("id", "transactionManager");
		bean.setAttribute("class",
				"org.springframework.orm.hibernate3.HibernateTransactionManager");
		// p:sessionFactory-ref="sessionFactory"
		return bean;
	}

	/**
	 * @param ns
	 * @return <bean id="hibernateTemplate" ......
	 */
	private Element createHibernateTemplateElement(Namespace ns) {
		Element e = new Element("bean", ns);
		e.setAttribute("id", "hibernateTemplate");
		e.setAttribute("class",
				"org.springframework.orm.hibernate3.HibernateTemplate");
		e.setAttribute("scope", "singleton");

		Element p = new Element("property", ns);
		p.setAttribute("name", "sessionFactory");
		p.setAttribute("ref", "sessionFactory");
		e.addContent(p);
		return e;
	}

	/**
	 * @param aop
	 *            namespace
	 * @return <aop:config proxy-target-class="true"> ......
	 */
	private Element createAopConfig(Namespace aop) {
		Element e = new Element("config", aop);
		e.setAttribute("proxy-target-class", "true");

		Element ad = new Element("advisor", aop);
		ad.setAttribute("pointcut", "execution(* com.gwtjs.dao.impl.*.*(..))");
		ad.setAttribute("advice-ref", "txAdvice");

		e.addContent(ad);
		return e;
	}

	// <context:annotation-config />
	private Element createAnnotationConfig(Namespace context) {
		return new Element("annotation-config", context);
	}

	// <context:component-scan ......
	private Element createComponentScan(Namespace context) {
		Element e = new Element("component-scan", context);
		e.setAttribute("base-package", "com.gwtjs");

		Element ce = new Element("exclude-filter", context);
		ce.setAttribute("type", "annotation");
		ce.setAttribute("expression",
				"org.springframework.stereotype.Controller");

		e.addContent(ce);
		return e;
	}

	/**
	 * @param outFile
	 *            输出路径加文件名 path+name
	 * @param propertyFile
	 *            数据库配置属性文件 file path / filename
	 */
	public void generatorHelper(String outFile, String propertyFile,
			List<String> hbms) {
		Namespace ns = Namespace
				.getNamespace("http://www.springframework.org/schema/beans");
		Namespace xsi = Namespace.getNamespace("xsi",
				"http://www.w3.org/2001/XMLSchema-instance");
		Namespace p = Namespace.getNamespace("p",
				"http://www.springframework.org/schema/p");
		Namespace aop = Namespace.getNamespace("aop",
				"http://www.springframework.org/schema/aop");
		Namespace tx = Namespace.getNamespace("tx",
				"http://www.springframework.org/schema/tx");
		Namespace context = Namespace.getNamespace("context",
				"http://www.springframework.org/schema/context");

		Element root = new Element("beans", ns);
		Document doc = new Document(root);
		root.addNamespaceDeclaration(xsi);
		root.addNamespaceDeclaration(p);
		root.addNamespaceDeclaration(aop);
		root.addNamespaceDeclaration(tx);
		root.addNamespaceDeclaration(context);

		Attribute namespace = getNamespace(xsi);
		root.setAttribute(namespace);

		root.addContent(getPlaceholderConfig(ns));
		System.out.println("property File: " + propertyFile);
		root.addContent(getDataSourceNode(ns, propertyFile));

		// <bean id="sessionFactory" ...
		root.addContent(createSessionFactoryElement(ns, hbms, propertyFile));

		// <bean id="hibernateTemplate" ... 这个bean没有作用，可以省略
		root.addContent(createHibernateTemplateElement(ns));

		// <bean id="transactionManager" ....
		root.addContent(createTransactionManager(ns, p));

		// tx:advice?
		root.addContent(createAdvice(tx));
		// <aop:config
		root.addContent(createAopConfig(aop));
		// <context:annotation-config />
		root.addContent(createAnnotationConfig(context));
		// <context:component-scan ......
		root.addContent(createComponentScan(context));

		Format format = Format.getPrettyFormat();
		format.setEncoding("UTF-8");
		format.setIndent("	");

		XMLOutputter out = new XMLOutputter(format);
		try {
			System.out.println(outFile);
			out.output(doc, new FileWriter(outFile));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
